﻿using System.Reflection;
using System.Runtime.CompilerServices;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using zijian666.Core;
using zijian666.DI.Features;

namespace zijian666.DI;

/// <summary>
/// 服务配置类扩展方法
/// </summary>
public static partial class ConfigureServicesExtensions
{
    private static readonly PreLogger _logger = new PreLogger(1024);
    /// <summary>
    /// 通过 <seealso cref="ServiceDescriptor"/> + <seealso cref="IServiceProvider"/> 来获取服务实例
    /// </summary>
    /// <param name="serviceDescriptor"></param>
    /// <param name="serviceProvider"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static object GetService(this ServiceDescriptor serviceDescriptor, IServiceProvider serviceProvider)
    {
        ArgumentNullException.ThrowIfNull(serviceDescriptor);
        ArgumentNullException.ThrowIfNull(serviceProvider);

        if (serviceDescriptor.ImplementationInstance is { } instance)
        {
            return instance;
        }
        if (serviceDescriptor.ImplementationFactory is { } factory)
        {
            return factory(serviceProvider);
        }
        if (serviceDescriptor.ImplementationType is { } type)
        {
            return ActivatorUtilities.CreateInstance(serviceProvider, type);
        }
        return null;
    }

    /// <summary>
    /// 获取已实现的服务
    /// </summary>
    public static IEnumerable<T> GetImplementationServices<T>(this IServiceCollection services)
        => services.GetImplementationServices().OfType<T>();

    /// <summary>
    /// 获取已实现的服务
    /// </summary>
    public static T? GetImplementationService<T>(this IServiceCollection services)
        => services.GetImplementationServices().OfType<T>().LastOrDefault();

    /// <summary>
    /// 获取所有已实现的服务
    /// </summary>
    public static IEnumerable<object> GetImplementationServices(this IServiceCollection services)
    {
        if (services is null)
        {
            yield break;
        }
        yield return services;
        foreach (var service in services)
        {
            if (service.ImplementationInstance is not { } instance)
            {
                continue;
            }
            if (instance is HostBuilderContext context)
            {
                yield return context.Configuration;
                yield return context.Properties;
                yield return context.HostingEnvironment;
                yield return context.HostingEnvironment.ContentRootFileProvider;
            }
            yield return instance;
        }
    }

    public static IServiceCollection AddComponents(this IServiceCollection services, IEnumerable<Type> types)
    {
        ArgumentNullException.ThrowIfNull(types);
        var filter = FeatureManager.Gets<IConfigureServicesFilter>().ToList();
        filter.ForEach(x => x.BeginAddComponents(services));
        // 分组规则
        // 1. 先按实现类型分组, (实现类型不同, 服务实例需要重新创建)
        // 2. 再按 服务Key 分组, (服务Key不同, 服务实例需要重新创建)
        // 3. 再按 生命周期分组, (生命周期不同, 服务实例需要重新创建)
        // 总结: 当 实现类型, 服务Key, 生命周期 都相同时, 服务实例是同一个
        foreach (var implementationType in types.Distinct())
        {
            var attr = implementationType.GetCustomAttributes<ComponentAttribute>(true)?.ToArray() ?? [];
            if (attr.Length == 0)
            {
                continue;
            }
            var isStartup = implementationType.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
                .Any(x => x.IsDefined(typeof(ConfigureServicesAttribute)) || x.IsDefined(typeof(ConfigureAttribute)) || x.IsDefined(typeof(HostStopAttribute)));

            if (isStartup)
            {
                // 如果是启动类, 先注册启动类
                AddStartup(services, implementationType);
                // 只有启动类才允许是抽象类或静态类, 其他服务会在后续注册时报错
                if (implementationType.IsAbstract && implementationType.IsSealed)
                {
                    continue;
                }
            }

            foreach (var keyed in attr.GroupBy(x => x.Key))
            {
                // 注册顺序为 单例, 作用域, 瞬时
                // 如果顺序不对, 可能会导致瞬时服务引用了作用域服务, 或者作用域服务引用了单例服务
                // 一般情况下瞬时服务和其他生命周期不应该同时存在, 但这里不做强制约束
                foreach (var lifetime in keyed.GroupBy(x => x.Lifetime).OrderBy(x => (int)x.Key))
                {
                    var added = false;
                    if (lifetime.Key != ServiceLifetime.Transient)
                    {
                        // 判断是否只有一个
                        if (lifetime.ElementAtOrDefault(1) is null)
                        {
                            lifetime.First().ConfigureServices(services, implementationType, false);
                            continue;
                        }

                        // 先注册实现类型
                        lifetime.First().Clone(implementationType).ConfigureServices(services, implementationType, false);
                        added = true;
                    }

                    // 再注册服务类型
                    foreach (var item in lifetime)
                    {
                        item.ConfigureServices(services, implementationType, added);
                    }
                }

            }
        }
        filter.ForEach(x => x.EndAddComponents(services));
        return services;
    }


    /// <summary>
    /// 添加 类特性中存在 <seealso cref="ComponentAttribute"/> 的类型为服务
    /// </summary>
    public static IServiceCollection AddComponents(this IServiceCollection services)
    {
        return services.AddComponents(AssemblyResolver.Default.GetTypes());
    }

    /// <summary>
    /// 使用自动注册服务功能, 添加类特性中存在 <seealso cref="ComponentAttribute"/> 的类型为服务
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <returns></returns>
    public static IHostBuilder AddComponents(this IHostBuilder hostBuilder) =>
        hostBuilder.ConfigureServices((_, x) => x.AddComponents());

    /// <summary>
    /// 将指定类型注册为配置类
    /// </summary>
    /// <typeparam name="TConfigure">服务配置类类型</typeparam>
    /// <param name="services"></param>
    /// <param name="args">动态参数</param>
    /// <returns></returns>
    public static IServiceCollection ConfigureServices<TConfigure>(this IServiceCollection services, params object[] args)
        => ConfigureServices(services, typeof(TConfigure), args);

    /// <summary>
    /// 将指定类型注册为配置类
    /// </summary>
    /// <param name="services"></param>
    /// <param name="instanceOrType">服务配置类实例或类型</param>
    /// <param name="args">动态参数</param>
    /// <returns></returns>
    public static IServiceCollection ConfigureServices(this IServiceCollection services, object instanceOrType, params object[] args)
    {
        if (instanceOrType is null)
        {
            throw new ArgumentNullException(nameof(instanceOrType));
        }
        // 注册 IHostedService
        HostedService.Register(services);


        // 创建启动器
        var launcher = new Launcher(instanceOrType);
        // 将启动器添加到服务集合
        services.AddSingleton<ILauncher>(launcher);
        // hostBuilder.ConfigureServices 是同步方法, 这里没有办法改为异步
        launcher.ConfigureServicesAsync(services, args, default).ConfigureAwait(false).GetAwaiter().GetResult();
        if (launcher.Startup != null)
        {
            services.Replace(ServiceDescriptor.Singleton(launcher.StartupType, launcher.Startup));
        }
        else if (!launcher.StartupType.IsAbstract)
        {
            services.TryAddSingleton(launcher.StartupType);
        }
        services.AddStartupFilter();
        return services;
    }

    /// <summary>
    /// 如果程序域中已加载 IStartupFilter 接口，则添加 IStartupFilter 实现类到服务集合 
    /// </summary>
    private static bool AddStartupFilter(this IServiceCollection services)
    {
        var filter = StartupFilter.Instance;
        if (filter is null)
        {
            return false;
        }
        if (services.Any(x => x.ServiceType == typeof(StartupFilter)))
        {
            return true;
        }
        services.AddSingleton<StartupFilter>();
        services.AddSingleton(StartupFilter.InterfaceType!, filter);
        return true;
    }


    /// <summary>
    /// 添加 Configure 回调
    /// </summary>
    /// <param name="services">配置</param>
    /// <param name="configure"> Configure 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IServiceCollection OnConfigure(this IServiceCollection services, Delegate configure, [CallerMemberName] string? serviceName = null)
    {
        services.TryAddSingleton<IHostedService, HostedService>();
        var launcher = new DynamicLauncher(serviceName, configure: configure);
        services.AddSingleton<ILauncher>(launcher);
        services.AddStartupFilter();
        return services;
    }

    /// <summary>
    /// 添加 Configure 回调
    /// </summary>
    /// <param name="services">配置</param>
    /// <param name="configure"> Configure 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IServiceCollection OnConfigure(this IServiceCollection services, Func<IServiceProvider, Task> configure, [CallerMemberName] string? serviceName = null)
        => OnConfigure(services, (Delegate)configure, serviceName);

    /// <summary>
    /// 添加 Configure 回调
    /// </summary>
    /// <param name="services">配置</param>
    /// <param name="configure"> Configure 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IServiceCollection OnConfigure(this IServiceCollection services, Action<IServiceProvider> configure, [CallerMemberName] string? serviceName = null)
        => OnConfigure(services, (Delegate)configure, serviceName);


    /// <summary>
    /// 添加 Configure 回调
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <param name="configure"> Configure 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IHostBuilder OnConfigure(this IHostBuilder hostBuilder, Delegate configure, [CallerMemberName] string? serviceName = null)
        => hostBuilder.ConfigureServices((_, x) => x.OnConfigure(configure, serviceName));

    /// <summary>
    /// 添加 Configure 回调
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <param name="configure"> Configure 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IHostBuilder OnConfigure(this IHostBuilder hostBuilder, Func<IServiceProvider, Task> configure, [CallerMemberName] string? serviceName = null)
        => OnConfigure(hostBuilder, (Delegate)configure, serviceName);

    /// <summary>
    /// 添加 Configure 回调
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <param name="configure"> Configure 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IHostBuilder OnConfigure(this IHostBuilder hostBuilder, Action<IServiceProvider> configure, [CallerMemberName] string? serviceName = null)
        => OnConfigure(hostBuilder, (Delegate)configure, serviceName);

    /// <summary>
    /// 添加 Stop 回调
    /// </summary>
    /// <param name="services">配置</param>
    /// <param name="stop"> Stop 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IServiceCollection OnStop(this IServiceCollection services, Delegate stop, [CallerMemberName] string? serviceName = null)
    {
        services.TryAddSingleton<IHostedService, HostedService>();
        var launcher = new DynamicLauncher(serviceName, stop: stop);
        services.AddSingleton<ILauncher>(launcher);
        return services;
    }

    /// <summary>
    /// 添加 Stop 回调
    /// </summary>
    /// <param name="services">配置</param>
    /// <param name="stop"> Stop 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IServiceCollection OnStop(this IServiceCollection services, Func<IServiceProvider, Task> stop, [CallerMemberName] string? serviceName = null)
        => OnStop(services, (Delegate)stop, serviceName);

    /// <summary>
    /// 添加 Stop 回调
    /// </summary>
    /// <param name="services">配置</param>
    /// <param name="stop"> Stop 回调</param>
    /// <param name="serviceName">服务名称，默认从执行方法名中推断</param>
    public static IServiceCollection OnStop(this IServiceCollection services, Action<IServiceProvider> stop, [CallerMemberName] string? serviceName = null)
        => OnStop(services, (Delegate)stop, serviceName);

    /// <summary>
    /// 添加启动类
    /// </summary>
    public static IServiceCollection AddStartup(this IServiceCollection services, Type startupType)
        => AddStartup(services, (object)startupType);

    /// <summary>
    /// 添加启动类
    /// </summary>
    public static IServiceCollection AddStartup<T>(this IServiceCollection services) => AddStartup(services, typeof(T));

    /// <summary>
    /// 添加启动类
    /// </summary>
    public static IServiceCollection AddStartup(this IServiceCollection services, object startup)
    {
        if (startup is null)
        {
            throw new ArgumentNullException(nameof(startup));
        }
        return ConfigureServices(services, startup, []);
    }

    /// <summary>
    /// 添加启动类
    /// </summary>
    /// <typeparam name="TStartup"></typeparam>
    /// <param name="hostBuilder"></param>
    /// <returns></returns>
    public static IHostBuilder AddStartup<TStartup>(this IHostBuilder hostBuilder)
        => AddStartup(hostBuilder, typeof(TStartup), null);

    /// <summary>
    /// 添加启动类
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <param name="startup"></param>
    /// <returns></returns>
    public static IHostBuilder AddStartup(this IHostBuilder hostBuilder, object startup)
    {
        if (startup is null)
        {
            throw new ArgumentNullException(nameof(startup));
        }

        return hostBuilder.AddStartup(startup.GetType(), startup);
    }

    /// <summary>
    /// 添加启动类
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <param name="startupType">启动类类型</param>
    /// <param name="startup">启动类实例</param>
    /// <returns></returns>
    public static IHostBuilder AddStartup(this IHostBuilder hostBuilder, Type startupType, object? startup = null)
    {
        if (hostBuilder is null)
        {
            throw new ArgumentNullException(nameof(hostBuilder));
        }

        if (startupType is null)
        {
            throw new ArgumentNullException(nameof(startupType));
        }

        if (startup is Type type && startup.GetType() == startupType)
        {
            startupType = type;
            startup = null;
        }

        var filter = FeatureManager.Gets<IConfigureServicesFilter>().ToList();
        filter.ForEach(x => x.BeginAddStartup(hostBuilder, startupType));
        hostBuilder.ConfigureServices((context, services) =>
        {
            // 将各种必要的服务添加到服务集合
            if (startup is not null)
            {
                services.TryAdd(ServiceDescriptor.Singleton(startupType, startup));
            }
            services.TryAddSingleton(context);
            services.TryAddSingleton(hostBuilder);
            ConfigureServices(services, startupType, []);
        });
        filter.ForEach(x => x.EndAddStartup(hostBuilder, startupType));
        return hostBuilder;
    }

    /// <summary>
    /// 执行所有注册的 Configure 方法 (方法名为'Configure'或拥有特性<seealso cref="ConfigureAttribute"/>的方法
    /// </summary>
    /// <param name="serviceProvider"></param>
    /// <param name="args"></param>
    public static void Configure(this IServiceProvider serviceProvider, params object[] args)
        => ConfigureAsync(serviceProvider, args).Wait();

    /// <summary>
    /// 执行所有注册的 Configure 方法 (方法名为'Configure'或拥有特性<seealso cref="ConfigureAttribute"/>的方法
    /// </summary>
    /// <param name="serviceProvider"></param>
    /// <param name="args"></param>
    public static async Task ConfigureAsync(this IServiceProvider serviceProvider, params object[] args)
    {
        _ = _logger.TryRedrect(serviceProvider, nameof(ConfigureServicesExtensions));

        var launcher = serviceProvider.GetServices<ILauncher>();
        if (launcher is null)
        {
            return;
        }

        args ??= [];
        var cancellationToken = args.OfType<CancellationToken>().FirstOrDefault();
        foreach (var startup in launcher)
        {
            await startup.ConfigureAsync(serviceProvider!, args, cancellationToken);
        }
    }

    /// <summary>
    /// 执行所有注册的 Configure 方法 (方法名为'Configure'或拥有特性<seealso cref="ConfigureAttribute"/>的方法
    /// </summary>
    public static async Task ConfigureAsync(this IHost host, params object[] args)
        => await host.Services.ConfigureAsync(args is { Length: > 0 } ? args.Append(host).ToArray() : new object[] { host });



    /// <summary>
    /// 替换服务集合中最后一个指定类型的服务实现。
    /// </summary>
    /// <typeparam name="TService">要替换的服务类型。</typeparam>
    /// <param name="services">服务集合。</param>
    /// <param name="implementationType">新的服务实现类型。</param>
    /// <param name="lifetime">服务的生命周期，如果未指定，则自动判断。</param>
    /// <remarks>
    /// 如果服务集合中不存在指定类型的服务，则直接添加新的服务描述符。
    /// </remarks>
    public static void ReplaceLast<TService>(this IServiceCollection services, Type implementationType, ServiceLifetime? lifetime = null)
            where TService : class
    {
        var descriptor = services.LastOrDefault(x => x.ServiceType == typeof(TService) && !x.IsKeyedService());
        if (descriptor is null)
        {
            services.Add(new ServiceDescriptor(typeof(TService), implementationType, lifetime ?? ServiceLifetime.Singleton));
            return;
        }
        Replace<TService>(services, [descriptor], (p, d) =>
        {
            p = ReplacedService<TService>.ProxyServiceProvider(p, d);
            return ActivatorUtilities.CreateInstance(p, implementationType);
        }, lifetime);
    }

    /// <summary>
    /// 替换服务集合指定类型的服务实现。
    /// </summary>
    /// <typeparam name="TService">要替换的服务类型。</typeparam>
    /// <param name="services">服务集合。</param>
    /// <param name="implementationType">新的服务实现类型。</param>
    /// <param name="lifetime">服务的生命周期，如果未指定，则自动判断。</param>
    /// <remarks>
    /// 如果服务集合中不存在指定类型的服务，则直接添加新的服务描述符。
    /// </remarks>
    public static void ReplaceAll<TService>(this IServiceCollection services, Type implementationType, ServiceLifetime? lifetime = null)
          where TService : class
    {
        var descriptors = services.Where(x => x.ServiceType == typeof(TService) && !x.IsKeyedService()).ToArray();
        if (descriptors.Length == 0)
        {
            services.Add(new ServiceDescriptor(typeof(TService), implementationType, lifetime ?? ServiceLifetime.Singleton));
            return;
        }
        Replace<TService>(services, descriptors, (p, d) =>
        {
            p = ReplacedService<TService>.ProxyServiceProvider(p, d);
            return ActivatorUtilities.CreateInstance(p, implementationType);
        }, lifetime);
    }

    /// <summary>
    /// 替换指定服务描述符的服务注册，并使用指定的工厂方法创建服务实例。
    /// </summary>
    private static void Replace<TService>(this IServiceCollection services, ServiceDescriptor[] serviceDescriptors, Func<IServiceProvider, ServiceDescriptor[], object> factory, ServiceLifetime? lifetime)
            where TService : class
    {
        foreach (var descriptor in serviceDescriptors)
        {
            lifetime ??= descriptor.Lifetime;
            if (descriptor.Lifetime > lifetime)
            {
                throw new InvalidOperationException($"无法替换服务 '{typeof(TService).FullName}' 因为新服务的生命周期不能大于被替换的服务 {descriptor.Lifetime} -> {lifetime}");
            }
            services.Remove(descriptor);
            var replacedServiceType = typeof(ReplacedService<TService>);
            services.Add(new ServiceDescriptor(replacedServiceType, p =>
            {
                return ActivatorUtilities.CreateInstance(p, replacedServiceType, descriptor);
            }, descriptor.Lifetime));
        }

        services.Add(new ServiceDescriptor(typeof(TService), p => factory(p, serviceDescriptors), lifetime ?? ServiceLifetime.Singleton));
    }

    /// <summary>
    /// 用于创建被替换服务的实例
    /// </summary>
    class ReplacedService<T>
    {
        private Lazy<object?> _value;

        public ReplacedService(IServiceProvider services, ServiceDescriptor serviceDescriptor)
        {
            ArgumentNullException.ThrowIfNull(services);
            ArgumentNullException.ThrowIfNull(serviceDescriptor);
            Services = services;
            ServiceDescriptor = serviceDescriptor;
            _value = new Lazy<object?>(() => serviceDescriptor.GetService(services) is T t ? t : default);
        }

        public object? Value => _value.Value;

        public IServiceProvider Services { get; }
        public ServiceDescriptor ServiceDescriptor { get; }

        public static IServiceProvider ProxyServiceProvider(IServiceProvider services, ServiceDescriptor[] descriptors)
            => new ServiceProviderProxy(services, descriptors);

        class ServiceProviderProxy : IServiceProvider
        {
            private readonly IServiceProvider _services;
            private readonly HashSet<ServiceDescriptor> _descriptors;
            public ServiceProviderProxy(IServiceProvider services, ServiceDescriptor[] descriptor)
            {
                _services = services;
                _descriptors = descriptor.ToHashSet();
            }
            public object? GetService(Type serviceType)
            {

                if (serviceType == typeof(T))
                {
                    var replaced = _services.GetServices<ReplacedService<T>>()
                            .Where(x => _descriptors.Contains(x.ServiceDescriptor))
                            .LastOrDefault();
                    return replaced?.Value;
                }
                if (serviceType == typeof(IEnumerable<T>))
                {
                    var source = _services.GetServices<ReplacedService<T>>()
                            .Where(x => _descriptors.Contains(x.ServiceDescriptor))
                                .Select(x => x.Value)
                                .ToArray();
                    var array = Array.CreateInstance(typeof(T), source.Length);
                    Array.Copy(source, 0, array, 0, source.Length);
                    return array;
                }
                if (serviceType == typeof(IServiceProvider))
                {
                    return _services;
                }
                return _services.GetService(serviceType);
            }
        }
    }

    internal static bool IsKeyedService(this ServiceDescriptor descriptor)
    {
        var prop = descriptor.GetType().GetProperty(nameof(IsKeyedService), BindingFlags.Instance | BindingFlags.Public);
        return prop?.GetValue(descriptor) is true;
    }

}
